package lynch.map 
{
	import com.greensock.easing.Back;
	import com.greensock.easing.Linear;
	import com.greensock.TweenNano;
	import Elegant.display.Container;
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;
	import lynch.core.Core;
	import lynch.enum.MapInfoEnum;
	
	/**
	 * the map of the game
	 * @author 小痛
	 */
	public class WorldMap extends Container 
	{
		/** the ratio of mini map divide by world map */
		private static var ratio:Number;
		
		private var currentMapInfo:MapInfoEnum;
		private var mosaic:MosaicMap = new MosaicMap();
		private var miniMap:MiniMap = new MiniMap();
		private var lastX:int;
		private var lastY:int;
		private var tilePool:TilePool;
		private var activeTiles:Dictionary;
		private var helpRect:Rectangle;
		
		/**
		 * tile x y
		 */
		private var cacheBounds:Point = new Point(1, 1);
		
		/**
		 * tile x y
		 */
		private var readyRect:Rectangle = new Rectangle();
		
		public function WorldMap() 
		{
			super();
			activeTiles = new Dictionary();
			tilePool = new TilePool();
			tilePool.mosaicMap = mosaic;
		}
		
		//--------------------initialize--------------------
		public function initializeInfo(mapInfo:MapInfoEnum):void
		{
			currentMapInfo = mapInfo;
			
			//---update ratio---
			ratio = mapInfo.miniMapTexture.width / (Core.config.tileWidth * mapInfo.mapVTiles);
			mosaic.updateInfo(mapInfo.miniMapTexture, ratio, mapInfo.mapVTiles, mapInfo.mapHTiles);
			tilePool.updateInfo(mapInfo.mapID);
			
			helpRect = new Rectangle(0, 0, 
					-Core.config.tileWidth * mapInfo.mapVTiles + Core.config.stageWidth,
					-Core.config.tileHeight * mapInfo.mapHTiles + Core.config.stageHeight);
			
			//---update texture---
			miniMap.updateInfo(mapInfo.miniMapTexture);
		}
		
		//--------------------convert-----------------------
		public function screenToWorld(px:int, py:int):Point
		{
			return new Point(px - x, py - y);
		}
		
		public function worldToScreen(px:int, py:int):Point
		{
			return new Point(px + x, py + y);
		}
		
		/**
		 * world map x y to tile x y
		 */ 
		public function worldToTile(px:uint, py:uint):Point
		{
			return new Point((px / Core.config.tileWidth) >> 0, (py / Core.config.tileHeight) >> 0);
		}
		
		/**
		 * tile x y to world map x y
		 */
		public function tileToworld(px:uint, py:uint):Point
		{
			return new Point(px * Core.config.tileWidth, py * Core.config.tileHeight);
		}
		
		public function big2MiniMap(px:uint, py:uint):Point
		{
			return new Point(px * ratio, py * ratio);
		}
		
		public function mini2bigMap(px:uint, py:uint):Point
		{
			return new Point(px / ratio, py / ratio);
		}
		
		//--------------------move-------------------------
		/**
		 * world x y
		 * @param	tx	世界坐标X
		 * @param	ty	世界坐标Y
		 */
		public function flashTo(tx:int, ty:int):void 
		{
			clearActiveTiles();
			
			var pobj:Object = checkXY(-tx, -ty);
			tx = pobj.x;
			ty = pobj.y;
			
			var p:Point = worldToTile(-tx, -ty);
			var wp:Point = worldToTile(Core.config.stageWidth, Core.config.stageHeight);
			var tvn:int = mapVTiles;
			var thn:int = mapHTiles;
			var sx:int = readyRect.x = Math.max(p.x - cacheBounds.x, 0);
			var sy:int = readyRect.y = Math.max(p.y - cacheBounds.y, 0);
			var fx:int = readyRect.width = Math.min(p.x + wp.x + cacheBounds.x, mapVTiles);
			var fy:int = readyRect.height = Math.min(p.y + wp.y + cacheBounds.y, mapHTiles);
			var tw:int = Core.config.tileWidth;
			var th:int = Core.config.tileHeight;
			
			var gt:Function = tilePool.getTile;
			for (var i:int = sx; i < fx; i++)
			{
				var gain:int = i % thn + ((i / thn) >> 0) * thn;
				
				for (var j:int = sy; j < fy; j++) 
				{
					var index:int = j * tvn + gain;
					var t:Tile = gt(index);
					t.x = i * tw;
					t.y = j * th;
					addChild(t);
					
					activeTiles[index] = t;
				}
			}
			
			lastX = x = tx;
			lastY = y = ty;
		}
		
		/**
		 * world x y
		 * @param	tx	世界坐标X
		 * @param	ty	世界坐标Y
		 * @param	time
		 */
		public function moveTo(tx:int, ty:int, duration:Number):void
		{
			var obj:Object = checkXY(tx, ty);
			if (x == tx && y == ty)
				return;
			
			obj.ease = Linear.easeNone;
			obj.onComplete = updateTiles;
			new TweenNano(this, duration, obj);
		}
		
		protected function checkXY(tx:int, ty:int):Object
		{
			//---limit left & top---
			if (tx > 0) tx = 0;
			if (ty > 0) ty = 0;
			
			//---limit right & bottom---
			if (tx < helpRect.width) tx = helpRect.width;
			if (ty < helpRect.height) ty = helpRect.height;
			
			return { x:tx, y:ty };
		}
		
		//------------------update------------------------
		/**
		 * you'd better not use this function directly
		 * the tiles update after move complete
		 */
		public function updateTiles():void
		{
			//getter / setter are too slow
			var x:int = this.x;
			var y:int = this.y;
			
			var p:Point = big2MiniMap(x, y);
			miniMap.updateMiniMapPos(p);
			
			var tw:int = Core.config.tileWidth;
			var th:int = Core.config.tileHeight;
			
			//差了多少块地砖
			var rx:Number = (x - lastX) / tw, ry:Number = (y - lastY) / th;
			var tvn:int = mapVTiles;
			var thn:int = mapHTiles;
			
			var sx:int, sy:int, fx:int, fy:int, i:int, j:int, gain:int, id:int, t:Tile, rdx:int, rdy:int, rdfx:int, rdfy:int, index:int;
			var gt:Function = tilePool.getTile;
			var rr:Rectangle = readyRect;
			var wp:Point = worldToTile(Core.config.stageWidth, Core.config.stageHeight);
			
			//-------------------------X----------------------------
			if (rx)
			{
				var cx:int = wp.x + (cacheBounds.x * 2) - (rr.width - rr.x);
				//向左走,地图向右
				if (rx > 0)
				{
					sx = rr.x - rx;
					if (sx < helpRect.x) sx = helpRect.x;
					
					fx = rr.x;
					readyRect.x = sx;
					
					rdx = rr.width - rx + cx;
					rdfx = rr.width;
					readyRect.width = rdx;
					
				} else if (rx < 0)
				{
					fx = rr.width - rx;
					if (fx > tvn) fx = tvn;
					
					sx = rr.width;
					readyRect.width = fx;
					
					rdx = rr.x;
					rdfx = rr.x - rx - cx;
					readyRect.x = rdfx;
				}
				
				fy = rr.height;
				for (i = sx; i < fx; ++i) 
				{
					gain = i % thn + ((i / thn) >> 0) * thn;
					for (j = rr.y; j < fy; ++j) 
					{
						index = gain + j * tvn;
						t = gt(index);
						t.x = tw * i;
						t.y = th * j;
						addChild(t);
						
						activeTiles[index] = t;
					}
				}
				
				//remove unuseful tiles
				for (i = rdx; i < rdfx; ++i)  
				{
					gain = i % thn + ((i / thn) >> 0) * thn;
					for (j = rr.y; j < fy; ++j) 
					{
						index = gain + j * tvn;
						t = activeTiles[index];
						removeChild(t);
						delete activeTiles[index];
					}
				}
			}
			//-------------------------Y----------------------------
			
			if (ry != 0)
			{
				//if
				var cy:int = wp.y + (cacheBounds.y * 2) - (rr.height - rr.y);
				
				//向上走,地图向下
				if (ry > 0)
				{
					sy = rr.y - ry;
					if (sy < helpRect.y) sy = helpRect.y;
					
					fy = rr.y;
					readyRect.y = sy;
					
					rdy = rr.height - ry + cy;
					rdfy = rr.height;
					readyRect.height = rdy;
					
				} else if (ry < 0)
				{
					fy = rr.height - ry;
					if (fy > thn) fy = thn;
					
					sy = rr.height;
					readyRect.height = fy;
					
					rdy = rr.y;
					rdfy = rr.y - ry - cy;
					readyRect.y = rdfy;
				}
				
				fx = rr.width;
				for (i = rr.x; i < fx; ++i) 
				{
					gain = i % thn + ((i / thn) >> 0) * thn;
					for (j = sy; j < fy; ++j) 
					{
						index = gain + j * tvn;
						t = gt(index);
						t.x = tw * i;
						t.y = th * j;
						addChild(t);
						
						activeTiles[index] = t;
					}
				}
				
				//remove unuseful tiles
				for (i = rr.x; i < fx; ++i)  
				{
					gain = i % thn + ((i / thn) >> 0) * thn;
					for (j = rdy; j < rdfy; ++j) 
					{
						index = gain + j * tvn;
						t = activeTiles[index];
 						removeChild(t);
						delete activeTiles[index];
					}
				}
			}
			
			lastX = x;
			lastY = y;
		}
		
		//-------------------------------
		
		/** return the instance of MiniMap */
		public function getMiniMap():MiniMap 
		{
			return miniMap;
		}
		
		/**
		 * clear all tiles on map
		 */
		public function clearActiveTiles():void 
		{
			removeChildren();
			
			var rec:Function = tilePool.recover;
			for each (var item:Tile in activeTiles) 
			{
				//if the pool is full
				if (rec(item) == false)
					break;
			}
			activeTiles = new Dictionary();
		}
		
		//-------------------------getter / setter---------------------
		/**	get current map id */
		public function get mapID():int { return currentMapInfo.mapID; }
		/**	get the num of tiles in v */
		public function get mapVTiles():int { return currentMapInfo.mapVTiles; }
		/**	get the num of tiles in H*/
		public function get mapHTiles():int { return currentMapInfo.mapHTiles; }
		/**	get current map name */
		public function get mapName():String { return currentMapInfo.mapName; }
		
	}
}